package cn.my.framework.activiti;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.RuntimeServiceImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.Execution;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * Created by zhangl_lc on 2017/7/25.
 */
@Component("activitiUtils")
public class ActivitiUtils {
    @Autowired
    ProcessEngine processEngine;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    IdentityService identityService;
    @Autowired
    HistoryService historyService;

    //==============部署==============
    public Deployment deployProcess(String name, String classpathResource) {
        return repositoryService.createDeployment().addClasspathResource(classpathResource).name(name).deploy();
    }

    public Deployment deployProcess(String name, InputStream is, String resourceName) {
        DeploymentBuilder dmb = repositoryService.createDeployment();
        if (resourceName == null && is instanceof ZipInputStream) {
            dmb.addZipInputStream((ZipInputStream) is);
        } else {
            dmb.addInputStream(resourceName, is);
        }
        return dmb.name(name).deploy();
    }

    public Deployment deployProcess(String name, BpmnModel model, String resourceName) {
        return repositoryService.createDeployment().addBpmnModel(resourceName, model).name(name).deploy();
    }

    //============启动==============
    public void startProcessByKey(String processKey, String businessKey, Map<String, Object> variables,String starter) {
        if(starter != null){
            identityService.setAuthenticatedUserId(starter);
        }
        runtimeService.startProcessInstanceByKey(processKey, businessKey, variables);
    }

    public void startProcessByMessage(String message, String businessKey, Map<String, Object> variables,String starter) {
        if(starter != null){
            identityService.setAuthenticatedUserId(starter);
        }
        runtimeService.startProcessInstanceByMessage(message, businessKey, variables);
    }

    //=============得到重要对象===========
    public ProcessDefinitionEntity getProcessDefinitionEntity(String defId) {
        return (ProcessDefinitionEntity) repositoryService.getProcessDefinition(defId);
    }

    public ProcessDefinitionEntity getDeployedProcessDefinitionEntity(String defId) {
        return (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).
                getDeployedProcessDefinition(defId);
    }

    public TaskEntity getTaskEntity(String taskId){
        return (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
    }

    public Execution getExecution(String exeId) {
        return runtimeService.createExecutionQuery().executionId(exeId).singleResult();
    }

    public List<Execution> listExecution(String instId) {
        return runtimeService.createExecutionQuery().processInstanceId(instId).list();
    }

    public ActivityImpl getActivityImpl(String activityId,String defId){
        ProcessDefinitionEntity curProcessDef = getProcessDefinitionEntity(defId);
        return curProcessDef == null ? null : curProcessDef.findActivity(activityId);
    }

    /**
     * 流程节点跳转
     */
    public void jumpActivity(String curTaskId, String tarTaskId,Map<String, Object> variables) {
        TaskEntity curTask = (TaskEntity) taskService.createTaskQuery().taskId(curTaskId).singleResult();
        ExecutionEntity curExecution = (ExecutionEntity) getExecution(curTask.getExecutionId());

        ProcessDefinitionEntity curProcessDef = getProcessDefinitionEntity(curExecution.getProcessDefinitionId());

        ActivityImpl curActivity = curProcessDef.findActivity(curTaskId);
        ActivityImpl targetActivity = curProcessDef.findActivity(tarTaskId);

        ((RuntimeServiceImpl) runtimeService).getCommandExecutor().execute(new Command<Void>() {
            @Override
            public Void execute(CommandContext commandContext) {
                if(variables != null){
                    curExecution.setVariables(variables);
                }
                curExecution.setEventSource(curActivity);
                curExecution.setActivity(curActivity);

                commandContext.getTaskEntityManager().findTasksByExecutionId(curTask.getExecutionId()).forEach(t ->{
                    t.fireEvent("complete");
                    commandContext.getTaskEntityManager()
                            .deleteTask(t, "跳转", false);
                });

                curExecution.executeActivity(targetActivity);

                /*curTask.setExecutionId(null);
                taskService.saveTask(curTask);
                *//*taskService.deleteTask(curTask.getId(), true);*//*
                commandContext.getTaskEntityManager().deleteTask(curTask.getId(),"跳转删除",false);*/
                return null;
            }
        });
    }

    //============查询相关==========
    public HistoricProcessInstance getHisProcessInstance(String instId){
        return historyService.createHistoricProcessInstanceQuery().processInstanceId(instId).singleResult();
    }

    public List<HistoricActivityInstance> listHisActivities(String instId) {
        return historyService.createHistoricActivityInstanceQuery().processInstanceId(instId).
                //orderByHistoricActivityInstanceEndTime().asc().
                list();
    }


    /**
     * 生成流程执行图
     */
    public void genProcessInstanceImage(String instId, OutputStream os) {
        try {
            //  获取历史流程实例
            HistoricProcessInstance hisProcessInstance = getHisProcessInstance(instId);

            if (hisProcessInstance != null) {
                // 获取流程定义
                ProcessDefinitionEntity processDefinition = getDeployedProcessDefinitionEntity(hisProcessInstance.getProcessDefinitionId());

                // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
                List<HistoricActivityInstance> hisActivityList = listHisActivities(instId);

                // 已执行的节点ID集合
                List<String> hisActivities = new ArrayList<>();
                //获取已经执行的节点ID
                for (HistoricActivityInstance activityInstance : hisActivityList) {
                    hisActivities.add(activityInstance.getActivityId());
                }

                // 获取流程走过的线
                List<String> hisFlows = listHisFlows(processDefinition, hisActivityList);

                BpmnModel bpmnModel = repositoryService
                        .getBpmnModel(hisProcessInstance.getProcessDefinitionId());
                // 获取流程图图像字符流
                ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
                //配置字体
                InputStream imageStream = pec.generateDiagram(bpmnModel, "png", hisActivities, hisFlows, "宋体", "微软雅黑", "黑体", null, 2.0);
                IOUtils.copy(imageStream,os);
                os.close();
                imageStream.close();
            }
        } catch (Exception e) {}
    }

    /**
     * 获取流程走过的线路
     */
    public List<String> listHisFlows(ProcessDefinitionEntity processDefinition,
            List<HistoricActivityInstance> hisActivityList) {
        List<String> hisFlows = new ArrayList<>();
        for (int i = 0; i < hisActivityList.size() - 1; i++) {// 对历史流程节点进行遍历
            ActivityImpl activityImpl = processDefinition
                    .findActivity(hisActivityList.get(i).getActivityId());// 得到节点定义的详细信息
            List<ActivityImpl> sameStartTimeNodes = new ArrayList<>();// 用以保存后需开始时间相同的节点
            ActivityImpl sameActivityImpl1 = processDefinition
                    .findActivity(hisActivityList.get(i + 1).getActivityId());
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < hisActivityList.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = hisActivityList.get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = hisActivityList.get(j + 1);// 后续第二个节点
                if (activityImpl1.getStartTime().equals(activityImpl2.getStartTime())) {
                    // 如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2 = processDefinition.findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {
                    // 有不相同跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();// 取出节点的所有出去的线
            for (PvmTransition pvmTransition : pvmTransitions) {
                // 对所有的线进行遍历
                ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    hisFlows.add(pvmTransition.getId());
                }
            }
        }
        return hisFlows;
    }

    //============设计相关==========
}